package king.dominic.jlibrary.util;

import android.Manifest;
import android.content.Context;
import android.net.ConnectivityManager;
import android.support.annotation.RequiresPermission;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import king.dominic.jlibrary.http.FastJsonConverterFactory;
import king.dominic.jlibrary.http.SSLSocketFactoryCompat;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.GzipSink;
import okio.Okio;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * Created by king on 2017/4/1.
 * HttpUtil
 */

public class HttpUtil {

    private static final int CONNECT_TIMEOUT = 100;
    private static final String TOKEN = "token";
    private static X509TrustManager x509;

    private HttpUtil() {
    }

    public static Interceptor interceptor(final Map<String, String> header) {
        return new Interceptor() {
            @Override
            public okhttp3.Response intercept(Chain chain) throws IOException {
                Request original = chain.request();
                Request.Builder builder = original.newBuilder();
                Set<String> keys = header.keySet();
                for (String key : keys) {
                    builder.header(key, header.get(key));
                }
                Request request = builder.method(original.method(), original.body())
                        .build();
                return chain.proceed(request);
            }
        };
    }

    public static OkHttpClient okHttpClient(Interceptor interceptor) {
        return new OkHttpClient.Builder()
                .sslSocketFactory(new SSLSocketFactoryCompat(X509()), X509())
                .addInterceptor(interceptor)
                .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                .build();
    }

    private static X509TrustManager X509() {
        if (x509 == null) {
            synchronized (HttpUtil.class) {
                if (x509 == null){
                    x509 = x509TrustManager();
                }
            }
        }
        return x509;
    }

    private static X509TrustManager x509TrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[]{};
            }
        };
    }

    public static Retrofit retrofitFastJson(OkHttpClient okHttpClient, String host) {
        return new Retrofit.Builder()
                .addConverterFactory(FastJsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(host)
                .client(okHttpClient)
                .build();
    }

    public static Retrofit retrofit(OkHttpClient okHttpClient, String host) {
        return new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
//                .addConverterFactory(StringConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(host)
                .client(okHttpClient)
                .build();
    }

    public static OkHttpClient setCertificate(OkHttpClient okHttpClient, String certificate) {
        OkHttpClient.Builder builder = okHttpClient.newBuilder();
        return setCertificate(builder, certificate).build();
    }

    public static OkHttpClient.Builder setCertificate(OkHttpClient.Builder builder, String certificate) {
        return setCertificates(builder, new Buffer().writeUtf8(certificate).inputStream());
    }

    public static OkHttpClient.Builder setCertificateFromAssets(Context context, OkHttpClient.Builder builder, String certificate) throws IOException {
        return setCertificates(builder, context.getAssets().open(certificate));
    }

    /**
     * 设置证书
     *
     * @param builder      OkHttpClient.Builder
     * @param certificates 证书输入流
     * @return OkHttpClient.Builder
     */
    public static OkHttpClient.Builder setCertificates(OkHttpClient.Builder builder, InputStream... certificates) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));

                try {
                    if (certificate != null)
                        certificate.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            SSLContext sslContext = SSLContext.getInstance("TLS");

            TrustManagerFactory trustManagerFactory =
                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            trustManagerFactory.init(keyStore);
            sslContext.init
                    (
                            null,
                            trustManagers,
                            new SecureRandom()
                    );

            return builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return builder;
    }

    /**
     * 检测网络连接
     *
     * @param context 上下文
     * @return 0 没有网络 1 非wifi 2 wifi
     */
    @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
    public static int checkConnect(Context context) {
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (manager.getActiveNetworkInfo() == null) {
            return Connect.NONE;
        } else {
            int type = manager.getActiveNetworkInfo().getType();
            switch (type) {
                case ConnectivityManager.TYPE_WIFI:
                    return Connect.WIFI;
                default:
                case ConnectivityManager.TYPE_MOBILE:
                    return Connect.MOBILE;
            }
        }
    }

    public static RequestBody gzip(final RequestBody body) {
        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return body.contentType();
            }

            @Override
            public long contentLength() {
                return -1; // We don't know the compressed length in advance!
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                body.writeTo(gzipSink);
                gzipSink.close();
            }
        };
    }

    public interface Connect {
        int NONE = -1;
        int MOBILE = 0;
        int WIFI = 1;
    }
}
